// $Id: Project.cs 67 2008-05-07 21:07:39Z nvivo $

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Schema;

namespace DBLGen
{
	public sealed class Project
	{
		private const string ProjectNamespace = "http://dblgen/project";

		#region Project Instance Fields and Properties

		private string _filePath;
		private TableCollection _tables;
		private SettingsManager _settingsManager;

		private PluginInfo _importPlugin;
		private PluginInfo _exportPlugin;

		public string FilePath
		{
			get
			{
				return _filePath;
			}
		}

		public TableCollection Tables
		{
			get
			{
				if (_tables == null)
					_tables = new TableCollection(this);

				return _tables;
			}
		}

		public SettingsManager SettingsManager
		{
			get
			{
				if (_settingsManager == null)
					_settingsManager = new SettingsManager();

				return _settingsManager;
			}
		}

		public PluginInfo ImportPlugin
		{
			get
			{
				return _importPlugin;
			}
			set
			{
				_importPlugin = value;
			}
		}

		public PluginInfo ExportPlugin
		{
			get
			{
				return _exportPlugin;
			}
			set
			{
				_exportPlugin = value;
			}
		}

		public string Name
		{
			get
			{
				if (File.Exists(FilePath))
					return Path.GetFileNameWithoutExtension(FilePath);
				else
					return "New Project";
			}
		}

		#endregion

		#region Table Importing Logic

		public void Import(IEnumerable<Table> tables, ImportBehavior behaviour)
		{
			switch (behaviour)
			{
				case ImportBehavior.Replace:
					// DO NOT ENABLE THIS OPTION BECAUSE ITS OVERWRITE ALL CUSTOM METHODS
					// ReplaceTablesWith(tables);
					break;
				case ImportBehavior.Merge:
					MergeTablesFrom(tables);
					break;
			}
		}

		private void ReplaceTablesWith(IEnumerable<Table> tables)
		{
			foreach (Table newTable in tables)
			{
				Table oldTable = Tables.Find(newTable.Schema, newTable.Name);

				if (oldTable != null)
					Tables.Remove(oldTable);
			}

			Tables.AddRange(tables);
		}

		private void MergeTablesFrom(IEnumerable<Table> tables)
		{
			///////////////////////////////////////////////////////////////////
			TableCollection col = new TableCollection();
			col.Items = new List<Table>(tables);
			List<Table> remove = new List<Table>();

			foreach (Table t in Tables)
			{ 
				Table n = col.Find(t.Schema, t.Name);
				if (n == null)
					remove.Add(t);
					// Tables.Remove(t);
			}

			Console.WriteLine(remove.Count);
			if (remove.Count > 0)
			{
				List<Table> newSource = new List<Table>();
				foreach (Table item in remove)
				{
					Tables.Remove(item);
				}
			}
			///////////////////////////////////////////////////////////////////
			


			foreach (Table newTable in tables)
			{
				Table oldTable = Tables.Find(newTable.Schema, newTable.Name);

				if (oldTable != null)
					oldTable.MergeFrom(newTable);
				else
					Tables.Add(newTable);
			}
		}

		#endregion

		#region Save

		public void Save()
		{
			if (String.IsNullOrEmpty(FilePath))
				throw new InvalidOperationException("Cannot save a new project. Use Save(string) to specify the file path.");

			Save(FilePath);
		}

		public void Save(string path)
		{
			string fullPath = Path.GetFullPath(path);
			string oldPath = _filePath;

			using (FileStream fs = File.Open(fullPath, FileMode.Create, FileAccess.Write))
			{
				try
				{
					_filePath = fullPath;
					Save(fs);
				}
				catch
				{
					_filePath = oldPath;
					throw;
				}
				finally
				{
					fs.Close();
				}
			}
		}

		private void Save(Stream s)
		{
			XmlTextWriter writer = new XmlTextWriter(s, Encoding.UTF8);
			writer.Formatting = Formatting.Indented;

			writer.WriteStartDocument();

			SaveToXml(writer);

			writer.WriteEndDocument();

			writer.Flush();
		}

		#endregion

		#region Load

		public static Project Load(string filePath)
		{
			filePath = Path.GetFullPath(filePath);

			XmlSchema schema = RM.LoadSchema("DBLGen.Schemas.DBLGen.xsd");

			XmlReaderSettings settings = new XmlReaderSettings();
			settings.ValidationType = ValidationType.Schema;
			settings.Schemas.Add(schema);

			Project p;

			using (FileStream fs = File.OpenRead(filePath))
			{
				using (XmlReader reader = XmlReader.Create(fs, settings))
				{
					XPathDocument doc;

					doc = new XPathDocument(reader);

					XPathNavigator nav = doc.CreateNavigator();
					nav.MoveToFirstChild();

					p = new Project();
					p._filePath = filePath;

					nav.MoveToFollowing("/project", nav.NamespaceURI);
					p.LoadFromXml(nav);

					reader.Close();
				}

				fs.Close();
			}

			return p;
		}

		#endregion

		#region Xml Persistence

		private void LoadFromXml(XPathNavigator navigator)
		{
			XPathNavigator localNav;

			// load import plugin

			localNav = navigator.SelectSingleNode("plugins/importPlugin/@type");

			if (localNav != null)
			{
				Type importPluginType = Type.GetType(localNav.Value);

				if (importPluginType != null)
					_importPlugin = new PluginInfo(importPluginType);
			}

			// load export plugin

			localNav = navigator.SelectSingleNode("plugins/exportPlugin/@type");

			if (localNav != null)
			{
				Type exportPluginType = Type.GetType(localNav.Value);

				if (exportPluginType != null)
					_exportPlugin = new PluginInfo(exportPluginType);
			}

			// load plugin settings

			_settingsManager = SettingsManager.LoadFromXml(navigator);

			// load the tables

			XPathNodeIterator ni = navigator.Select("tables/table");

			while (ni.MoveNext())
			{
				Table t = Table.LoadFromXml(ni.Current);
				Tables.Add(t);
			}
		}

		private void SaveToXml(XmlWriter writer)
		{
			writer.WriteStartElement("p", "project", ProjectNamespace);

			// save plugins

			if (ImportPlugin != null || ExportPlugin != null)
			{
				writer.WriteStartElement("plugins");

				if (ImportPlugin != null)
				{
					writer.WriteStartElement("importPlugin");
					writer.WriteAttributeString("type", ImportPlugin.PluginType.AssemblyQualifiedName);
					writer.WriteEndElement();
				}

				if (ExportPlugin != null)
				{
					writer.WriteStartElement("exportPlugin");
					writer.WriteAttributeString("type", ExportPlugin.PluginType.AssemblyQualifiedName);
					writer.WriteEndElement();
				}

				writer.WriteEndElement();
			}

			// save settings
			SettingsManager.SaveToXml(writer);

			// save tables
			if (Tables.Count > 0)
			{
				writer.WriteStartElement("tables");

				foreach (Table t in Tables)
					t.SaveToXml(writer);

				writer.WriteEndElement();
			}

			writer.WriteEndElement();
		}

		#endregion
	}
}
